/** @file    DiscoveryUdpBundle.cpp
 *  @author  Alessandro Polo
 *  @version $Id: DiscoveryUdpBundle.cpp 2696 2010-06-22 17:38:04Z alex $
 *  @brief
 * File containing methods for the wosh::services::DiscoveryUdpBundle class.
 * The header for this class can be found in DiscoveryUdpBundle.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "DiscoveryUdpBundle.h"
 #include "DiscoveryUdpImpl.h"

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/Fact.h>
 #include <core/WoshKernel.h>
 #include <framework/network/SocketUdp.h>

 
using namespace std;
namespace wosh {
 namespace services {

 WOSH_REGISTER(wosh::services::DiscoveryUdpBundle, "wosh::interfaces::services::DiscoveryUdp", _DiscoveryUdp_VERSION, _static_DiscoveryUdpBundle1 )
 WOSH_REGISTER(wosh::services::DiscoveryUdpBundle, "wosh::BundleGeneric", _DiscoveryUdp_VERSION, _static_DiscoveryUdpBundle2 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

DiscoveryUdpBundle::DiscoveryUdpBundle()
	: BundleGeneric() {
	BundleGeneric::setName( _DiscoveryUdp_NAME, false );
	Properties.update( _KEY_Version, _DiscoveryUdp_VERSION );
	Requirements.add<wosh::network::SocketUdp>();

	Log(LOG_DEBUG, " Configuring Discovery worker.." );
	this->discWorker = new DiscoveryUdpImpl(*this);
	discWorker->setBindAddressPort( _DiscoveryUdp_DEFAULT_BindAddressPort );
	discWorker->setBroadcastAddressPort( _DiscoveryUdp_DEFAULT_BroadcastAddressPort );
	discWorker->setNotifyFrequency( _DiscoveryUdp_DEFAULT_NotifyFrequency );

	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	Properties.set( _DiscoveryUdp_KEY_BindAddressPort,		_DiscoveryUdp_DEFAULT_BindAddressPort,		Permission::RW_RW_R__() );
	Properties.set( _DiscoveryUdp_KEY_BroadcastAddressPort,	_DiscoveryUdp_DEFAULT_BroadcastAddressPort,	Permission::RW_RW_R__() );
 	Properties.set( _Discovery_KEY_NotifyFrequency,			_DiscoveryUdp_DEFAULT_NotifyFrequency,		Permission::RW_RW_R__() );
	Properties.monitor(_DiscoveryUdp_KEY_BindAddressPort);
	Properties.monitor(_DiscoveryUdp_KEY_BroadcastAddressPort);
	Properties.monitor(_Discovery_KEY_NotifyFrequency);

	Log(LOG_DEBUG, " Registering methods.." );
	MethodRequest* mmDiscovery = Methods.create<MethodRequest>( _Discovery_METHOD_discover, "force sending discovery message", Permission::R_XR_XR__() );
	mmDiscovery->setMethod( this, (MethodRequestPtr)&DiscoveryUdpBundle::mmDoDiscovery );

	setBundleState(Bundle::STATE_CREATED, false);
 }

DiscoveryUdpBundle::~DiscoveryUdpBundle() {
	Log(LOG_DEBUG, ":~DiscoveryUdpBundle() : Destroying.." );
	if ( isBundleRunning() ) {
		Log(LOG_WARNING, "~DiscoveryUdpBundle() : Destroying while Bundle is running! Trying to stop.." );
		bundleStop();
	 }
	delete this->discWorker; this->discWorker = NULL;
	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL

WRESULT DiscoveryUdpBundle::bundleStart() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STARTING) )
		return WRET_ERR_WRONG_STATE;
	WRESULT ret = BundleGeneric::start_SynchThread( this->discWorker );
	// BUNDLE-STATE (STARTED) will be updated async by WORKER, through call: thread_event()
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT DiscoveryUdpBundle::bundleStop() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STOPPING) )
		return WRET_ERR_WRONG_STATE;
	WRESULT ret = BundleGeneric::stop_SynchThread( this->discWorker );
	// BUNDLE-STATE (STOPPED) will be updated async by WORKER, through call: thread_event()
	return ret;
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT

bool DiscoveryUdpBundle::updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source ) {
	WRESULT ret = WRET_OK;
	if ( property_current.getKey() == _DiscoveryUdp_KEY_BindAddressPort ) {
		ret = this->discWorker->setBindAddressPort( value_proposed.toString() );
	 }
	else if ( property_current.getKey() == _DiscoveryUdp_KEY_BroadcastAddressPort ) {
		int errors = this->discWorker->setBroadcastAddressPort( value_proposed.toString() );
		if ( errors < 0 )
			ret = WRET_ERR_PARAM;
	 }
	else if ( property_current.getKey() == _Discovery_KEY_NotifyFrequency ) {
		this->discWorker->setNotifyFrequency( value_proposed.toInteger(this->discWorker->getNotifyFrequency()) );
	 }
	else // forward processing to base class
		return BundleGeneric::updatingProperty(do_update, value_proposed, property_current, source);
	return WSUCCEEDED(ret);
 }

////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT DiscoveryUdpBundle::doAnnounceLocalHost() {
	return this->discWorker->sendDiscoveryUdpMessage();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* DiscoveryUdpBundle::mmDoDiscovery( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_DEBUG, "mmDoDiscovery(%s)", option.c_str() );
	WRESULT ret = this->discWorker->sendDiscoveryUdpMessage();
	return request->replyResponse(ret);
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace services
}; // namespace wosh
